package com.net.xpay.web.secutiry;

import com.google.common.base.Strings;
import com.net.common.exception.BusinessException;
import com.net.common.util.SerializationUtil;
import com.net.xpay.common.domain.CheckCode;
import com.net.xpay.common.domain.User;
import com.net.xpay.common.domain.UserProfile;
import com.net.xpay.common.enums.user.CheckCodeType;
import com.net.xpay.common.enums.user.ClientFrom;
import com.net.xpay.common.manager.CheckCodeManager;
import com.net.xpay.common.manager.UserManager;
import com.net.xpay.common.manager.UserProfileManager;
import com.net.xpay.common.service.CheckCodeService;
import com.net.xpay.common.validator.CheckCodeValidator;
import com.net.xpay.core.constant.PoseidonErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.mindrot.jbcrypt.BCrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;

/**
  on 01/02/2018.
 */
@Slf4j
@Component
public class FormLoginAuthenticationProvider implements AuthenticationProvider {
    private static final Logger LOGGER = LoggerFactory.getLogger(FormLoginAuthenticationProvider.class);
    @Autowired
    private UserManager userManager;
    @Autowired
    private CheckCodeValidator checkCodeValidator;
    @Autowired
    private CheckCodeManager checkCodeManager;
    @Autowired
    private UserProfileManager userProfileManager;
    @Autowired
    private CheckCodeService checkCodeService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        FormLoginToken token = (FormLoginToken) authentication;
        if (Strings.isNullOrEmpty(token.getMobile())) {
            throw new WebAuthenticationException(PoseidonErrorCode.MOBILE_ERROR, "手机号格式不正确");
        } else if (token.getClientFrom() == null) {
            throw new WebAuthenticationException(PoseidonErrorCode.BUSINESS_ERROR, " 登陆来源错误");
        }

        try {
            return doAuthentication(token);
        } catch (AuthenticationException e) {
            throw e;
        } catch (BusinessException e) {
            throw new WebAuthenticationException(e.getErrorCode(), e.getErrorMessage());
        }
        catch (Exception e) {
            LOGGER.warn("user authenticated failed|mobile={}|password={}|checkCode={}",
                    token.getMobile(), token.getPassword(), token.getCheckCode(), e);
            throw new WebAuthenticationException(PoseidonErrorCode.BUSINESS_ERROR, "登陆服务暂时不可用,请稍后重试");
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return FormLoginToken.class.isAssignableFrom(authentication);
    }

    private Authentication doAuthentication(FormLoginToken token) throws AuthenticationException {
        User user = userManager.findByMobile(token.getMobile());
        if (user == null) {
            throw new WebAuthenticationException(PoseidonErrorCode.MOBILE_NOT_FOUND, "手机号没找到");
        }

        verifyCredential(token, user);

        return new Operator(user, token);
    }

    private void verifyCredential(FormLoginToken token, User user) throws AuthenticationException {
        UserProfile userProfile = userProfileManager.getById(user.getId());
        if (Boolean.TRUE.equals(userProfile.getStrictLogin())) {
            //严格认证模式 同时验证密码和验证码
            if (token.getClientFrom() == ClientFrom.PC_WEB_2) {
                //新页面
                verifyPassword(token, user);
                //发验证码
                if (Strings.isNullOrEmpty(token.getCheckCode())) {
                    checkCodeService.sendCheckCode(user.getMobile(), CheckCodeType.LOGIN, "");
                    throw new WebAuthenticationException(PoseidonErrorCode.STRICT_LOGIN_ERROR, "验证码已发送");
                }

                verifyCheckCode(token);
            } else {
                verifyPassword(token, user);
                verifyCheckCode(token);
            }

        } else {
            switch (token.getLoginMethod()) {
                case CHECK_CODE:
                    verifyCheckCode(token);
                    break;
                case PASSWORD:
                    verifyPassword(token, user);
                    break;
                case REGISTER_AUTO:
                    verifyPassword(token, user);
                    break;
                default:
                    throw new WebAuthenticationException(PoseidonErrorCode.BUSINESS_ERROR, "登陆方式不正确,请稍后重试");
            }
        }

    }

    private void verifyCheckCode(FormLoginToken token) throws AuthenticationException {
        try {
            CheckCode checkCode = checkCodeManager.findByMobileAndType(token.getMobile(), CheckCodeType.LOGIN);

            checkCodeValidator.validateCheckCode(checkCode, token.getCheckCode());
        } catch (BusinessException e) {
            log.warn("verifyCredential verifyCheckCode psw wrong token={}|checkCode={}", SerializationUtil.obj2String(token), token.getCheckCode());
            throw new WebAuthenticationException(PoseidonErrorCode.CHECK_CODE_ERROR, e.getErrorMessage());
        }
    }

    private void verifyPassword(FormLoginToken token, User user) throws AuthenticationException {
        String password = token.getPassword() == null ? "" : token.getPassword();
        if (!BCrypt.checkpw(password, user.getPassword())) {
            log.warn("verifyCredential verifyPassword psw wrong token={}|password={}", SerializationUtil.obj2String(token), password);
            throw new WebAuthenticationException(PoseidonErrorCode.PASSWORD_ERROR, "密码不正确");
        }
    }
}